package YAPserver.gameObjects;

import YAPenvironment.YAPconstant;
import YAPserver.behaviours.BounceBehaviour;
import YAPserver.behaviours.BounceDynamic;
import YAPserver.behaviours.BounceFromPlayer;
import YAPserver.behaviours.BounceMirror;
import YAPserver.logic.Game;
/**
 * The slider is a paddle that can move left or right to bat away gameBalls or to catch mods. Each player has a slider that he or she uses to defend their wall.
 * A slider is essentially a line that can move along another line.
 * @author Daniel
 *
 */
public class Slider extends AbstractWall{
	
	private double m_speed;
	private final Line m_boundary; //the boundaries do not change during the game
	private Point m_speedVector;
	private GameWall m_wall;
	
	/**
	 * Constructs a slider with the wall as its boundary. The length of the slider is set to a the slidersize constant in {@link YAPconstant}
	 * @param wall wall to which this slider slides across
	 */
	public Slider(GameWall wall){
		super(wall);
		m_boundary = wall.getLine();
		setLength(this.m_line.getLength()*YAPconstant.C_SLIDERSIZE);
		m_bounceBehaviour = new BounceDynamic();
		m_speed = 30*YAPconstant.C_SLIDERSPEED/Game.m_fps;
		//initial speed in two calculations for better understanding
//		m_speed = m_line.getLength()*m_speed/25.;
		m_speedVector = new Point(Math.cos(m_line.getAngle())*m_speed, Math.sin(m_line.getAngle())*m_speed);
		Point middle = new Point(m_middlePoint);
		calcMiddlePoint(m_line.getStartPoint(), m_line.getEndPoint());
		setMiddlePoint(middle);
		m_wall = wall;
	}
	
	/**
	 * Sets the speed of this slider.
	 * @param speed new speed
	 */
	public void setSpeed(double speed){
		//now to normalise the speed; *30 because this was the original fps
		speed = 30*speed/Game.m_fps;
		m_speed = speed;
	}
	
	/**
	 * Sets the length of this slider.
	 * @param length new length
	 */
	public void setLength(double length){
		if(length > m_boundary.getLength()){
			length = m_boundary.getLength();
		}
		if(length < YAPconstant.C_MINSLIDERSIZE){
			length = YAPconstant.C_MINSLIDERSIZE;
		}
		m_line.setLength(length);
		Point startPoint = m_line.getStartPoint();
		Point endPoint = m_line.getEndPoint();
		setStartEndPoints(startPoint, endPoint);
		m_line.setStartPoint(m_startPoint);
		if(cornerVicinity(m_boundary.getStartPoint(), m_line.getStartPoint())){
			return;
		}else{
			cornerVicinity(m_boundary.getEndPoint(), m_line.getEndPoint());
		}
	}
	
	@Override
	public void setMiddlePoint(Point newPoint){
		Point translation = newPoint.subtract(m_middlePoint);
		Point startPoint = m_startPoint.add(translation);
		Point endPoint = m_endPoint.add(translation);
		setStartEndPoints(startPoint, endPoint);
		m_line.setStartPoint(m_startPoint);
		calcMiddlePoint(m_startPoint, m_endPoint);
	}
	
	/**
	 * This method checks if the start and end points are still within the boundaries
	 * and resets them if they are not
	 * @param startPoint point to check if in boundary
	 * @param endPoint point to check if in boundary
	 */
	private void setStartEndPoints(Point startPoint, Point endPoint){
		if(startPoint.isElementOneD(m_boundary)){
			if(endPoint.isElementOneD(m_boundary)){
				//if both new points are still within the boundary then everything's ok
				m_startPoint = startPoint;
				m_endPoint = endPoint;
				return;
			}else{
				//endPoint not element boundary
				m_line.setEndPoint(m_boundary.getEndPoint());
				m_startPoint = m_line.getStartPoint();
				m_endPoint = m_line.getEndPoint();
				return;
			}
		}else{
			//startPoint not element boundary
			m_line.setStartPoint(m_boundary.getStartPoint());
			m_startPoint = m_line.getStartPoint();
			m_endPoint = m_line.getEndPoint();
			setStartEndPoints(m_startPoint, m_endPoint);
			return;
		}
	}
	/**
	 * Moves the slider left (according to the GUI)
	 */
	public void moveLeft() {
		Point startPoint = m_startPoint.add(m_speedVector);
		Point endPoint = m_endPoint.add(m_speedVector);
		setStartEndPoints(startPoint, endPoint);
		m_line.setStartPoint(m_startPoint);
		cornerVicinity(m_boundary.getEndPoint(), m_line.getEndPoint());
	}

	/**
	 * Moves the slider right (according to the GUI)
	 */
	public void moveRight() {
		Point startPoint = m_startPoint.subtract(m_speedVector);
		Point endPoint = m_endPoint.subtract(m_speedVector);
		setStartEndPoints(startPoint, endPoint);
		m_line.setStartPoint(m_startPoint);
		cornerVicinity(m_boundary.getStartPoint(), m_line.getStartPoint());
	}
	
	/**
	 * Checks whether two points are too close to each other or not. Typically the boundary end points with the slider end points.
	 * Then the bounceBehaviour changes to mirror instead of dynamic. This stops balls from getting stuck in corners.
	 * @param point1 point to test
	 * @param point2 point to test
	 * @return <code>true</code> only if a point is close to another point, otherwise returns <code>false</code>
	 * @see BounceBehaviour
	 */
	private boolean cornerVicinity(Point point1, Point point2){
		//TODO: clean up code
		if(m_bounceBehaviour instanceof BounceMirror){
			if(point1.distance(point2) < m_line.getLength()/4.){
				return true;
			}else{
				m_bounceBehaviour = new BounceDynamic();
				return false;
			}
		}
		if(m_bounceBehaviour instanceof BounceDynamic){
			if(point1.distance(point2) < m_line.getLength()/4.){
				m_bounceBehaviour = new BounceMirror();
				return true;
			}else{
				return false;
			}
		}
		if(m_bounceBehaviour instanceof BounceFromPlayer){
			if(point1.distance(point2) < m_line.getLength()/4.){
				((BounceFromPlayer)m_bounceBehaviour).setSliderBounceBehaviour(new BounceMirror());
				return true;
				//if nearCorner == true then the bounceBehaviour is already BounceMirror()
			}else{
				((BounceFromPlayer)m_bounceBehaviour).setSliderBounceBehaviour(new BounceDynamic());
				return false;
			}
		}
		return false;
	}
	
	/**
	 * Returns the speed vector of this slider.
	 * @return
	 */
	public Point getSpeedVector(){
		return this.m_speedVector;
	}
	
	/**
	 * Returns the wall of this slider
	 * @return the wall of this slider
	 */
	public GameWall getWall(){
		return m_wall;
	}
	/**
	 * Returns the wall to the right of this slider.
	 * @return the previous wall
	 */
	public GameWall getWallPrev(){
		return m_wall.getPrevWall();
	}
	/**
	 * Returns the wall to the left of this slider.
	 * @return the next wall
	 */
	public GameWall getWallNext(){
		return m_wall.getNextWall();
	}
	
	@Override
	public void setBounceBehaviour(BounceBehaviour b){
		m_bounceBehaviour = b;
		if(cornerVicinity(m_boundary.getStartPoint(), m_line.getStartPoint())){
			return;
		}else{
			cornerVicinity(m_boundary.getEndPoint(), m_line.getEndPoint());
		}
	}
}
